--[[Player
玩家类，处理玩家的移动等
]]
Player = class(EntityLiving)

Player.width = 0.4
Player.height = 1.75
Player.eyeHeight = 1.62

Player.survivalMode = 0
Player.creativeMode = 1

Player.fovOffset = 6
Player.fovStep = 1.5

Player.walkingSounds = {
"Project:grass1",
"Project:grass2",
"Project:grass3",
"Project:grass4"
}

function Player:init()
    EntityLiving.init(self)
    self.model = PlayerModel()
    self.handModel = HandModel()
    self.walkingScale = 0
    self:setSize(Player.width,Player.height)
    self.heightOffset = Player.eyeHeight
    self:resetPos()
    self.mode = Player.survivalMode
    self.walkingSound = 0
    self.selectBlock = {x = 0,y = 0,z = 0} -- 玩家指着的方块坐标
    self.lastSelect = {x = 0,y = 0,z = 0}
    self.destoryingTime = 0
    self.oBob,self.bob,self.oTilt,self.tilt = 0,0,0,0
    self.fovOffset,self.fovOffsetO = 0,0
    self.soundFunc = function()
        if self.walking and self.onGround then
            soundEngine:addMusic(Player.walkingSounds[self.walkingSound + 1])
            self.walkingSound = self.walkingSound + 1
            self.walkingSound = self.walkingSound % 4
        end
    end
    soundEngine:addCallBack(soundFunc)
    self.lastPos = {x = self.x,y = self.y,z = self.z}
    self.posUpdated = true
end

function Player:resetPos()
    local x,z = world.spawnX,world.spawnZ
    local y = world.height
    while y > 0 and world:isAirBlock(x,y,z) do
        y = y - 1
    end
    y = y + 3
    self:setPos(x + 0.5,y,z + 0.5)
end

-- 玩家位置周期性更新
function Player:tick()
    self.xo = self.x
    self.yo = self.y
    self.zo = self.z
    self.oBob = self.bob
    self.oTilt = self.tilt
    self.walkDistO = self.walkDist
    self.fovOffsetO = self.fovOffset
    
    local xa,za,jump = 0,0,false
    if control.keys.w then
        if control.keys.allways then
            if control.keys.left or control.keys.a then
                xa = xa - 1
            end
            if control.keys.right or control.keys.d then
                xa = xa + 1
            end
            if control.keys.forward then
                za = za - 1
            end
            if control.keys.backward or control.keys.s then
                za = za + 1
            end
        else
            za = za - 1
        end
    else
        if control.keys.d then xa = xa + 1 end
        if control.keys.a then xa = xa - 1 end
        if control.keys.s then za = za + 1 end
    end
    if control.keys.space or control.keys.jump then
        jump = true
    end
    
    self.inWater = self:isInWater()
    self.inLava = self:isInLava()
    
    if self.mode == Player.survivalMode then
        self.walking = vec2(xa,za):len() > 0
        if self.inWater then
            self:moveRelative(xa,za,Player.inLiquidMoveSpeed)
            self.yd = self.yd - Player.inLiquidGravity
            self:move(self.xd,self.yd,self.zd)
            
            self.bob = self.bob * 0.4
            if self.horizontalCollision or jump then
                self:jumpInLiquid()
            end
            
            self.xd = self.xd * Player.inWaterSpeedDecay
            self.yd = self.yd * Player.inWaterSpeedDecay * 1.1
            self.zd = self.zd * Player.inWaterSpeedDecay
        elseif self.inLava then
            self:moveRelative(xa,za,Player.inLiquidMoveSpeed)
            self.yd = self.yd - Player.inLiquidGravity
            self:move(self.xd,self.yd,self.zd)
            
            self.bob = self.bob * 0.2
            if self.horizontalCollision or jump then
                self:jumpInLiquid()
            end
            
            self.xd = self.xd * Player.inLavaSpeedDecay
            self.yd = self.yd * Player.inLavaSpeedDecay * 0.8
            self.zd = self.zd * Player.inLavaSpeedDecay
        else
            self:moveRelative(xa,za,self:getWalkingSpeed())
            
            if jump and (not control.hasJumped) and self.onGround then
                control.hasJumped = true
                self:jumpOnGround()
                if GameSetting.PlaySound then
                    soundEngine:addMusic("Game Sounds One:Jump")
                end
            end
            
            if self.horizontalCollision and self.onGround then
                if math.sqrt(self.xdo ^ 2 + self.zdo ^ 2) > 0.13 then
                    self:jumpOnGround()
                end
            end
            
            if self.walking and not soundEngine:hasMusic() then
                self.soundFunc()
            end
            
            local dist = vec2(self.xd,self.zd):len()
            local yc = math.atan(-self.yd * 0.2) * 15
            self.bob = self.bob + (dist - self.bob) * 0.4
            self.tilt = (yc - self.tilt) * 0.3
            
            self:move(self.xd,self.yd,self.zd)
            self.yd = self.yd - Player.gravity
            
            if self.onGround then
                self.xd = self.xd * Player.onGroundSpeedDelay
                self.zd = self.zd * Player.onGroundSpeedDelay
            end
            
            if self.horizontalCollision then
                self.xd = self.xd * 0.6
                self.zd = self.zd * 0.6
            end
        end
        
        if self.fovOffset > 0 then
            self.fovOffset = self.fovOffset - Player.fovStep
        end
    else
        self.walking = false
        self:moveRelative(xa,za,Player.moveSpeed * 2.5)
        if control.keys.up then
            self.yd = self.yd + 0.15
        end
        if control.keys.down then
            self.yd = self.yd - 0.15
        end
        
        self:move(self.xd,self.yd,self.zd)
        
        self.xd = self.xd * 0.7
        self.yd = self.yd * 0.8
        self.zd = self.zd * 0.7
        self.fallDist = 0
        
        if self.fovOffset < Player.fovOffset then
            self.fovOffset = self.fovOffset + Player.fovStep
        end
    end
    
    if self.y < -64 then
        self:die()
    end
    
    -- 岩浆扣血
    if self.inLava then
        self.health = self.health - 0.1
    else
        self.health = self.health + 0.01
    end
    
    self:processHealth()
    healthBar.health = self.health
    
    self.xd = self.xd * 0.91
    self.yd = self.yd * 0.91
    self.zd = self.zd * 0.91
    self:updatePos()
end

function Player:destoryBlock()
    local x,y,z = self.selectBlock.x,self.selectBlock.y,self.selectBlock.z
    if self.lastSelect then
        if self.lastSelect.x ~= x or self.lastSelect.y ~= y or self.lastSelect.z ~= z then
            self.destoryingTime = 0
            -- 上次的破坏坐标
            self.lastSelect = self.selectBlock
            return true
        end
    else
        self.lastSelect = self.selectBlock
    end
    if self.mode == Player.survivalMode then
        self.destoryingTime = self.destoryingTime + 1
        if self.destoryingTime < self.selectBlock.block.destoryTime then
            if GameSetting.EnableParticle and self.destoryingTime % 15 == 0 then
                for i = 1,5 do
                    pe:add(SmallParticle(x + 0.5,y + 0.5,z + 0.5,self.selectBlock.block.tex))
                end
            end
            return false
        end
    end
    -- 破坏时间置零
    self.destoryingTime = 0
    world:setBlock(x,y,z,Block.air.id)
    if not GameSetting.EnableParticle then return end
    local sd = 3
    for xx = 0,sd - 1 do
        for yy = 0,sd - 1 do
            for zz = 0,sd - 1 do
                local xp = x + (xx + 0.5) / sd
                local yp = y + (yy + 0.5) / sd
                local zp = z + (zz + 0.5) / sd
                pe:add(Particle(xp,yp,zp,xp-x-0.5,yp-y-0.5,zp-z-0.5,self.selectBlock.block.tex))
            end
        end
    end
    local item = self.selectBlock.block:getDroppedItem()
    if item then
        selector:addOneTile(item.id)
    end
    self.lastSelect = nil
    return true
end

function Player:updateSelect(a)
    local s = nil
    local ax = math.sin(player.yRot * piover180) * math.cos(-player.xRot * piover180) * selectMinStep
    local ay = math.sin(-player.xRot * piover180) * selectMinStep
    local az = -math.cos(player.yRot * piover180) * math.cos(-player.xRot * piover180) * selectMinStep
    local x = player.x + (player.x - player.xo) * a
    local y = player.y + (player.y - player.yo) * a
    local z = player.z + (player.z - player.zo) * a
    local px = 0
    local py = 0
    local pz = 0
    for i = 0,selectMaxLength,selectMinStep do
        px = math.floor(x + ax * i)
        py = math.floor(y + ay * i)
        pz = math.floor(z + az * i)
        if world:hasBlock(px,py,pz) and world:isSolidBlock(px,py,pz) then
            s = {}
            s.x,s.y,s.z = px,py,pz
            s.block = Block.blocks[world:getBlock(px,py,pz)]
            px = x + ax * i
            py = y + ay * i
            pz = z + az * i
            break
        end
    end
    if not s then self.selectBlock = nil return end
    local dist = 10000
    local d = math.abs(px - (s.x))
    if d < dist then
        dist = d
        s.f = Left
    end
    d = math.abs(px - (s.x + 1.0))
    if d < dist then
        dist = d
        s.f = Right
    end
    d = math.abs(py - s.y)
    if d < dist then
        dist = d
        s.f = Bottom
    end
    d = math.abs(py - (s.y + 1.0))
    if d < dist then
        dist = d
        s.f = Top
    end
    d = math.abs(pz - s.z)
    if d < dist then
        dist = d
        s.f = Back
    end
    d = math.abs(pz - (s.z + 1.0))
    if d < dist then
        dist = d
        s.f = Front
    end
    self.selectBlock = s
end

function Player:shouldShowBob()
    return self.mode == Player.survivalMode
end

function Player:changeMode()
    self.mode = 1 - self.mode
    if self.mode == Player.creativeMode then
        self.yd = self.yd + Player.jumpHeight * 0.2
    end
end

function Player:getWalkingSpeed()
    -- 在这里加方块检测
    if world:getBlock(math.floor(self.x),math.floor(self.y-2),math.floor(self.z)) == Block.soilSand.id then
        return Player.moveSpeed * 0.1
    end
    if self.onGround then
        return Player.moveSpeed
    else
        return Player.moveSpeed * 0.2
    end
end

function Player:render(a)
    pushMatrix()
    if self.walking == true then
        self.walkingScale = math.min(self.walkingScale + 0.1,1)
    else
        self.walkingScale = self.walkingScale * 0.9
    end
    -- local time = (self.walkDistO + (self.walkDist - self.walkDistO) * a) * 70
    -- local time = timer:getTime() * 160
    --[[
    if self.inWater or self.inLava then
        time = time * Player.inLiquidMoveSpeed
    else
        time = time * Player.moveSpeed
    end
      ]]
    local time = timer:getTime() / Player.moveSpeed
    
    local x = self.xo + (self.x - self.xo) * a
    local y = self.yo + (self.y - self.yo) * a
    local z = self.zo + (self.z - self.zo) * a
    
    translate(x,y - 0.4,z)
    scale(1,-1,1)
    scale(0.05)
    rotate(-self.yRot + 180,0,1,0)
    
    if GameSetting.ThirdPersonView then
        self.model:render(time,self.walkingScale,-self.xRot * piover180)
        resetMatrix()
        local xa,za = math.floor(self.x),math.floor(self.z)
        y = math.floor(self.aabb.y0)
        while world:isOpacityBlock(xa,y,za) do
            y = y - 1
        end
        y = y + 1
        if self.aabb.y0 - y <= 3 then
            translate(x,y + 0.01,z)
            rotate(90,1,0,0)
            tint(255, 255, 255, 100 * (1 - (self.aabb.y0 - y) / 3))
            spriteMode(CENTER)
            sprite("Project:shadow",0,0,0.7 * (1 - (self.aabb.y0 - y) / 3))
        end
    else
        self.handModel:render(-(self.xRot - 90) * piover180)
    end
    popMatrix()
end

function Player:die()
    alert("You died!")
    self:resetPos()
    self.health = self.maxHealth
end

-- 更新玩家坐标，用于刷新区块
function Player:updatePos()
    self.lastPos.y = self.y
    if self:distanceToSqr(self.lastPos) >= 192 then
        self.lastPos.x = self.x
        self.lastPos.z = self.z
        self.posUpdated = true
    end
end
